
#ifndef __MATCH_HPP__
#define __MATCH_HPP__

#include "utils.hpp"
#include "data_manager.hpp"
#include "game_room.hpp"
#include "online_user.hpp"
#include <list>
#include <condition_variable>

// 两个类: 等待队列的类实现 , 进行匹配的接口实现


// 1. 先设计一个等待队列
// 内部成员: 该队列使用list来进行实现, 为了保证线程安全: 此处要使用锁和条件变量
// 内部接口: 加入 , 移除 , 是否为空  , 等待 , 队列长度


template<class T>
class Match_queue
{
public:

    void push(const T& data)
    {
        // 1. 加入元素
        // 2. 唤醒等待队列中的线程
        std::unique_lock<std::mutex> lock(_mtx);
        _list.push_back(data);
        _cond.notify_all();
    }

    void pop(T& data)
    {
        std::unique_lock<std::mutex> lock(_mtx);
        if(_list.size())
        {
            data = _list.front();
            _list.pop_front();
        }
    }

    void remove(const T& data) 
    { 
        std::unique_lock<std::mutex> lock(_mtx);
        _list.remove(data); 
    }

    bool empty() 
    {
        std::unique_lock<std::mutex> lock(_mtx); 
        return _list.empty(); 
    }

    void wait() 
    { 
        std::unique_lock<std::mutex> lock(_mtx);
        _cond.wait(lock) ; 
    }

    size_t size() 
    {
        std::unique_lock<std::mutex> lock(_mtx);
        return _list.size() ; 
    }

private:
    std::list<T> _list;
    std::mutex _mtx;
    std::condition_variable _cond;
};


// 2. 实现匹配管理
// 私有成员 : 三个队列 , 对应三个线程 , 
//           房间管理的句柄(将匹配好的成员加入到房间中) , 在线用户管理句柄(检查匹配的双方是否都在线) , 用户管理表(获得用户的相关信息,尤其是得分)
// 成员函数 : 三个线程的入口函数 , 将用户加入到等待队列中 ,将用户从等待队列中移除


class Match
{
    void base_call_back(Match_queue<uint64_t>& mq)
    {
        // 1. 循环式的等待
        // 2. 元素个数 < 2进行等待
        // 3. 元素个数 > 2将两个用户拿出来
        // 4. 检查两个用户还在没在大厅中
        // 5. 创建房间, 将两个用户加入进去
        // 6. 广播
        while(1)
        {
            while(mq.size() < 2) 
                mq.wait();
            Log_ns::Log(Log_ns::Debug) << "人数够了, 开始匹配";
            uint64_t uid1 , uid2;
            mq.pop(uid1); 
            if(!_hall_manger->if_online(uid1))
            {
                Log_ns::Log(Log_ns::Debug) << uid1 << "没在大厅中";

                continue;
            }
            mq.pop(uid2);
            if(!_hall_manger->if_online(uid2))
            {
                Log_ns::Log(Log_ns::Debug) << uid2 << "没在大厅中";

                // 将uid1放回去
                mq.push(uid1);
                continue;
            }

            websocket_server::connection_ptr conn_ptr1 = _hall_manger->get_conn_by_id(uid1);
            if(!conn_ptr1)
            {
                Log_ns::Log(Log_ns::Debug) << uid1 << "用户链接无效";
                // 将uid1放回去
                mq.push(uid2);
                continue;
            }
            websocket_server::connection_ptr conn_ptr2 = _hall_manger->get_conn_by_id(uid2);
            if(!conn_ptr2)
            {
                Log_ns::Log(Log_ns::Debug) << uid2 << "用户链接无效";

                // 将uid1放回去
                mq.push(uid1);
                continue;
            }

            std::shared_ptr<Room> room_ptr = _rooms->create_room(uid1 , uid2);
            if(!room_ptr)
            {
                mq.push(uid1);
                mq.push(uid2);
                continue;
            }
            Json::Value resp;
            resp["optype"] = "match_success";
            resp["result"] = true;

            std::string resp_str;
            Json_util::serialization(resp , resp_str);
            conn_ptr1->send(resp_str);
            conn_ptr2->send(resp_str);
        }
    }

    void bronze_call_back() { base_call_back(_bronze_level_q) ; }

    void silver_call_back() { base_call_back(_silver_level_q) ; }

    void gold_call_back() { base_call_back(_gold_level_q) ; }
    
public:

    Match(Online_manger* hall_manger , Online_manger* room_manger , Room_manger* rooms , User_table* user_table)
    :_hall_manger(hall_manger) , _room_manger(room_manger) , _rooms(rooms) , _user_table(user_table),
    _bronze_th(std::thread(std::bind(&Match::bronze_call_back , this))) ,
    _silver_th(std::thread(std::bind(&Match::silver_call_back , this))) ,
    _gold_th(std::thread(std::bind(&Match::gold_call_back , this))) 
    {}

    bool add(uint64_t uid)
    {
        Json::Value usr;
        bool ret = _user_table->select_by_id(uid , usr);
        if(ret == false)
        {
            return false;
        }
        // 提取出分数
        size_t score = usr["score"].asUInt();
        if(score < 2000)
        {
            _bronze_level_q.push(uid);
        }
        else if(score >= 2000 && score < 3000)
        {
            _silver_level_q.push(uid);
        }
        else
        {
            _gold_level_q.push(uid);
        }
        Log_ns::Log(Log_ns::Debug) << uid << " : 用户开始匹配 , 当前匹配人数:" << _bronze_level_q.size();
        return true;
    }

    bool del(uint64_t uid)
    {
        Json::Value usr;
        bool ret = _user_table->select_by_id(uid , usr);
        if(ret == false)
        {
            return false;
        }
        // 提取出分数
        size_t score = usr["score"].asUInt();
        if(score < 2000)
        {
            _bronze_level_q.pop(uid);
        }
        else if(score >= 2000 && score < 3000)
        {
            _silver_level_q.pop(uid);
        }
        else
        {
            _gold_level_q.pop(uid);
        }
        Log_ns::Log(Log_ns::Debug) << uid << " : 用户取消匹配, 当前匹配人数:" << _bronze_level_q.size();

        return true;
    }

private:
    Match_queue<uint64_t> _bronze_level_q;
    Match_queue<uint64_t> _silver_level_q;
    Match_queue<uint64_t> _gold_level_q;

    std::thread _bronze_th;
    std::thread _silver_th;
    std::thread _gold_th;

    Online_manger* _hall_manger;
    Online_manger* _room_manger;
    Room_manger* _rooms;
    User_table* _user_table;
};


#endif